﻿package org.flashIso.engine.base {
	import flash.utils.getQualifiedClassName;
	
	import org.flashIso.engine.core.ILibrary;
	import org.flashIso.engine.core.ISerializableXML;
	import org.flashIso.engine.core.Point2D;
	import org.flashIso.engine.core.Point3D;
	import org.flashIso.engine.core.ValidateableObject;

	/**
	 * 
	 * IsoPerspective is an abstract representation of isometric perspective.
	 * Isometric perspective is described by specifying the angle
	 * between isometric axis (X, Y, Z) and a horizontal line.
	 * 
	 */
	public class IsoPerspective extends ValidateableObject implements ISerializableXML{

		private var _xAngle : Number;
		private var _yAngle : Number;
		private var _zAngle : Number;

		
		private var _xAngleRadians : Number;
		private var _yAngleRadians : Number;
		private var _zAngleRadians : Number;
		
		private var _sinX : Number;
		private var _sinY : Number;
		private var _sinZ : Number;
		
		private var _cosX : Number;
		private var _cosY : Number;
		private var _cosZ : Number;

		private var _tanX : Number;
		private var _tanY : Number;
		private var _tanZ : Number;

		private static var piOver180 : Number = Math.PI / 180;

		
		public function IsoPerspective(xAngle : Number = 25, yAngle : Number = 155, zAngle:Number = 90) {
			super();
			_xAngle = xAngle;
			_yAngle = yAngle;
			_zAngle = zAngle;
			validateNow();
		}

		override public function validateNow() : void {
			
			//cache values for better performance
			_xAngleRadians = _xAngle * piOver180;
			_yAngleRadians = _yAngle * piOver180;
			_zAngleRadians = _zAngle * piOver180;
			
			_sinX = Math.sin(_xAngleRadians);
			_sinY = Math.sin(_yAngleRadians);
			_sinZ = Math.sin(_zAngleRadians);
			
			_cosX = Math.cos(_xAngleRadians);
			_cosY = Math.cos(_yAngleRadians);
			_cosZ = Math.cos(_zAngleRadians);
			
			_tanX = _sinX / _cosX;
			_tanY = _sinY / _cosY;
			_tanZ = _sinZ / _cosZ;
			super.validateNow();
		}

		public function set xAngle(value : Number) : void {
			if (_xAngle != value) {
				_xAngle = value;
				triggerValidation();
			}
		}
		public function get xAngle(): Number {
			return _xAngle;
		}

		public function set yAngle(value : Number) : void {
			if (_yAngle != value) {
				_yAngle = value;
				triggerValidation();
			}
		}
		public function get yAngle(): Number {
			return _yAngle;
		}

		public function set zAngle(value : Number) : void {
			if (_zAngle != value) {
				_zAngle = value;
				triggerValidation();
			}
		}
		public function get zAngle(): Number {
			return _zAngle;
		}

		override public function toString() : String {
			return "[Perspective ( xAngle : " + _xAngle + " yAngle : " + _yAngle + " zAngle : " + _zAngle + ")]";
		}
		
		
		/**
		 * 
		 * Use this function to transform a point from 
		 * 3D coordinates to flash 2D coordinates.
		 * 
		 **/
		public function getProjection(point : Point3D) : Point2D {
			var point2D : Point2D = new Point2D();
			point2D.autoValidation = false;
			point2D.x = point.x * _cosX + point.y * _cosY + point.z * _cosZ;
			point2D.y = -point.z * _sinZ - point.y * _sinY - point.x * _sinX;
			point2D.autoValidation = true;
			return point2D;
		}

		/**
		 * 
		 * Use this function to transform a point from 
		 * 2D sprite coordinates to 3D coordinates.
		 * Note that the 3D returned point will always have z coordinate equal to 0.
		 * 
		 **/
		public function get3DCoordinates(p : Point2D) : Point3D {
			var point3D : Point3D = new Point3D();
			point3D.autoValidation = false;
			point3D.x = (-p.y - p.x * _tanY) / ((_tanX - _tanY) * _cosX);
			point3D.y = (-p.y - p.x * _tanX) / ((_tanY - _tanX) * _cosY);
			point3D.autoValidation = true;
			return point3D;
		}
		
		public function toXML() : XML {
			var xml:XML = new XML("<" + className + " />");
			xml.@xAngle = xAngle;
			xml.@yAngle = yAngle;
			xml.@zAngle = zAngle;			
			return xml;
		}
		
		public function fromXML(xml:XML, library:ILibrary = null) : void {
			xAngle = parseFloat(xml.@xAngle);
			yAngle = parseFloat(xml.@yAngle);
			zAngle = parseFloat(xml.@zAngle);
		}		
		
	}
}
